package utility_task

import (
	"Sailor/app/Sailor-console-server/internal/consts"
	"Sailor/app/Sailor-console-server/internal/dao"
	"Sailor/app/Sailor-console-server/internal/model/do"
	"Sailor/app/Sailor-console-server/internal/model/entity"
	"Sailor/app/Sailor-console-server/utility/utility_task/utils"
	"context"
	"fmt"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/jakecoffman/cron"
	"sync"
	"time"
)

var (
	// 定时任务调度管理器
	serviceCron *cron.Cron

	// 同一任务是否有实例处于运行中
	runInstance Instance

	// 任务计数-正在运行的任务
	taskCount TaskCount

	// 并发队列, 限制同时运行的任务数量
	concurrencyQueue ConcurrencyQueue
)

// ConcurrencyQueue 并发队列
type ConcurrencyQueue struct {
	queue chan struct{}
}

func (cq *ConcurrencyQueue) Add() {
	cq.queue <- struct{}{}
}

func (cq *ConcurrencyQueue) Done() {
	<-cq.queue
}

// TaskCount 任务计数
type TaskCount struct {
	wg   sync.WaitGroup
	exit chan struct{}
}

func (tc *TaskCount) Add() {
	tc.wg.Add(1)
}

func (tc *TaskCount) Done() {
	tc.wg.Done()
}

func (tc *TaskCount) Exit() {
	tc.wg.Done()
	<-tc.exit
}

func (tc *TaskCount) Wait() {
	tc.Add()
	tc.wg.Wait()
	close(tc.exit)
}

// Instance 任务ID作为Key
type Instance struct {
	m sync.Map
}

// 是否有任务处于运行中
func (i *Instance) has(key string) bool {
	_, ok := i.m.Load(key)

	return ok
}

func (i *Instance) add(key string) {
	i.m.Store(key, struct{}{})
}

func (i *Instance) done(key string) {
	i.m.Delete(key)
}

type SysTaskResult struct {
	Result     string
	Err        error
	SysTaskLog entity.SysTaskLog
}

// Initialize 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行
func Initialize(ctx context.Context) {
	InitJob()

	serviceCron = cron.New()
	serviceCron.Start()
	concurrencyQueue = ConcurrencyQueue{queue: make(chan struct{}, 500)}
	taskCount = TaskCount{sync.WaitGroup{}, make(chan struct{})}
	go taskCount.Wait()
	g.Log().Info(ctx, "开始初始化定时任务")
	taskNum := 0
	var taskList []entity.SysTask
	err := dao.SysTask.Ctx(ctx).Where(dao.SysTask.Columns().TaskStatus, consts.Enabled).Scan(&taskList)
	if err != nil {
		g.Log().Error(ctx, "获取任务列表失败", err)
	}
	for _, item := range taskList {
		err := Add(ctx, item)
		if err != nil {
			g.Log().Error(ctx, item.Uuid, item.TaskName, "添加任务到调度器失败", err)
		}
		taskNum++
	}
	g.Log().Info(ctx, "任务初始化完成", "taskNum:", taskNum)
}

// RemoveAndAdd 删除任务后添加
func RemoveAndAdd(ctx context.Context, taskModel entity.SysTask) error {
	//task.Remove(taskModel.Id)
	err := Add(ctx, taskModel)
	if err != nil {
		return err
	}
	return nil
}

// Remove 删除任务
func Remove(id string) {
	serviceCron.RemoveJob(id)
}

// Add 添加任务
func Add(ctx context.Context, taskModel entity.SysTask) error {
	taskFunc := CreateJob(ctx, taskModel)
	if taskFunc == nil {
		return gerror.New("创建任务处理Job失败,不支持的任务协议")
	}

	cronName := taskModel.Uuid
	err := utils.PanicToError(func() {
		serviceCron.AddFunc(taskModel.TaskCron, taskFunc, cronName)
	})
	if err != nil {
		g.Log().Error(ctx, "添加任务到调度器失败", err)
		return err
	}
	return nil
}

// ChangeStatus 改变任务状态
func ChangeStatus(uuid string, status int) (string, error) {
	_, err := dao.SysTask.Ctx(context.Background()).Where(dao.SysTask.Columns().Uuid, uuid).Update(do.SysTask{TaskStatus: status})
	if err != nil {
		return "", err
	}
	if status == consts.Enabled {
		err := AddTaskToTimer(uuid)
		if err != nil {
			return "", err
		}
	} else {
		Remove(uuid)
	}
	return "", nil
}

// Run 直接运行任务
func Run(taskModel entity.SysTask) {
	go CreateJob(context.Background(), taskModel)()
}

// CreateJob  创建任务处理Job
func CreateJob(ctx context.Context, taskModel entity.SysTask) cron.FuncJob {

	handler := CreateHandler(taskModel)
	if handler == nil {
		return nil
	}
	taskFunc := func() {

		// 如果是多任务, 则只允许一个实例运行
		if taskModel.TaskMulti == 0 && runInstance.has(taskModel.Uuid) {
			return
		}

		taskCount.Add()
		defer taskCount.Done()

		taskLogId := beforeExecJob(ctx, taskModel)
		if taskLogId == "" {
			return
		}

		concurrencyQueue.Add()
		defer concurrencyQueue.Done()
		g.Log().Info(ctx, fmt.Sprintf("开始执行任务命令:%s", taskModel.TaskInvokeTarget))
		taskResult := execJob(ctx, handler, taskModel, taskLogId)
		g.Log().Info(ctx, fmt.Sprintf("任务执行完成命令:%s", taskModel.TaskInvokeTarget))
		afterExecJob(ctx, taskResult, taskLogId)
	}
	return taskFunc
}

// CreateHandler 创建任务处理Handler
func CreateHandler(taskModel entity.SysTask) Handler {
	var handler Handler = nil
	switch taskModel.TaskTag {
	case "httpTask":
		handler = new(HTTPHandler)
	case "internalFuncTask":
		handler = new(InternalFuncHandler)
	case "shellTask":
		handler = new(ShellHandler)
	}
	return handler
}
func AddTaskToTimer(uuid string) error {
	ctx := context.Background()
	// 获取任务详情
	var task entity.SysTask
	err := dao.SysTask.Ctx(ctx).Where(dao.SysTask.Columns().Uuid, uuid).Scan(&task)
	if err != nil {
		g.Log().Error(ctx, "获取任务详情失败", err)
		return err
	}
	err = RemoveAndAdd(ctx, task)
	if err != nil {
		return err
	}
	return nil
}

// 任务前置操作
func beforeExecJob(ctx context.Context, taskModel entity.SysTask) (taskLogId string) {

	if taskModel.TaskMulti == 0 && runInstance.has(taskModel.Uuid) {
		taskLogId, _ := SysTaskLogServiceApp.CreateTaskLogByTask(ctx, taskModel, consts.Cancel)
		return taskLogId
	}
	taskLogId, err := SysTaskLogServiceApp.CreateTaskLogByTask(ctx, taskModel, consts.Running)
	if err != nil {
		g.Log().Error(ctx, "任务开始执行#写入任务日志失败", err)
		return
	}
	return taskLogId
}

// 任务执行后置操作
func afterExecJob(ctx context.Context, taskResult SysTaskResult, taskLogId string) {
	_, err := SysTaskLogServiceApp.UpdateTaskLogByTaskResult(ctx, taskLogId, taskResult)
	if err != nil {
		g.Log().Error(ctx, "任务结束#更新任务日志失败-", err)
	}
}

// 执行具体任务
func execJob(ctx context.Context, handler Handler, taskModel entity.SysTask, sysTaskLogId string) SysTaskResult {
	defer func() {
		if err := recover(); err != nil {
			g.Log().Error(ctx, "panic#task#service.go.bak:execJob#", err)
		}
	}()
	// 默认只运行任务一次
	var execTimes int8 = 1
	if taskModel.TaskRetryTimes > 0 {
		execTimes += int8(taskModel.TaskRetryTimes)
	}

	var taskLog entity.SysTaskLog
	errs := dao.SysTaskLog.Ctx(ctx).Where(dao.SysTaskLog.Columns().Uuid, sysTaskLogId).Scan(&taskLog)
	if errs != nil {
		g.Log().Error(ctx, "获取任务日志失败", errs)
		return SysTaskResult{Result: "", Err: errs, SysTaskLog: taskLog}
	}

	var i int8 = 0
	var output string
	var err error
	for i < execTimes {
		output, err = handler.Run(ctx, taskModel, sysTaskLogId)
		if err == nil {
			return SysTaskResult{Result: output, Err: err, SysTaskLog: taskLog}
		}
		i++
		if i < execTimes {
			g.Log().Error(ctx, fmt.Sprintf("任务执行失败#任务id-%s#重试第%d次#输出-%s#错误-%s", taskModel.Uuid, i, output, err))
			if taskModel.TaskRetryInterval > 0 {
				time.Sleep(time.Duration(taskModel.TaskRetryInterval) * time.Second)
			} else {
				// 默认重试间隔时间，每次递增1分钟
				time.Sleep(time.Duration(i) * time.Minute)
			}
		}
	}

	return SysTaskResult{Result: output, Err: err, SysTaskLog: taskLog}
}
